#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

#include <sys/types.h>
#include <dirent.h>

#include <iostream>
#include <string>


#include "android_node.h"


char *sample_image_urls[] = {
    "/sdcard/data/charles1.jpg",
    "/sdcard/data/charles2.jpg",
    "/sdcard/data/zeddy.png",
    "/sdcard/data/logan.jpg\n"
};


int AndroidNode::message_counter = 1000;
    
AndroidNode::AndroidNode(char* hostname, int port) : hostname(hostname), port(port)
{
    int mut = pthread_mutex_init(&request_deport_mtx, NULL);
    if(mut) {
        cout <<"Error AndroidNode():\n\tInitializing request_deport_mtx("<<mut<<") ...exiting"<<endl;
        exit(0);
    }
}
AndroidNode::~AndroidNode() 
{

}

int AndroidNode::open() {
    chanState = false;
    port = port;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { 
        cout <<"ERROR opening socket"<<endl;
        return -1;
    }
    server = gethostbyname(hostname.c_str());
    if (server == NULL) {
        cout <<"ERROR, no such host"<<endl;;
        return -1;
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(port);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
        cout <<"ERROR connecting"<<endl;
        return -1;
    }

    // ---Not sure why adb is not picking messages from HTC on time - 
    unsigned int opt = 4, one = 1;
    setsockopt(sockfd, IPPROTO_TCP,TCP_NODELAY,(void *)&opt, sizeof(opt));
    setsockopt(sockfd, IPPROTO_TCP,SO_KEEPALIVE,(void *)&opt, sizeof(opt));
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
 
    Message hMsg;
    int ret = recv_msg(&hMsg, 4);
    if((ret == TIMEOUT) || (ret == SOCKET_CLOSED)){
        cout <<" --- No Android Server - exiting....."<<endl;
        return -1;
    }
    cout <<"[ "<<hMsg.str()<<" ] ...."<<endl;
    chanState = true;

    // - start reading thread - 
    //pthread_create(&request_deport_t,NULL,&AndroidNode::_android_read_,(void*)this);
    //pthread_detach(request_deport_t);
    cout <<"Done Setting up android Transport ......"<<endl; 

    return SUCCESS;
}
    
int AndroidNode::send_msg(Message *hMsg) {
    if((hMsg->message_type() <= 0) || (hMsg->message_type() > ANDROID_USER_LIMIT)) {
        return MALFORMED_MSG;
    } 
    hMsg->add_entry("\n");
    int msg_len = hMsg->android_str().length();
    int n = write(sockfd,hMsg->android_str().c_str(),msg_len);
    if(n <= 0) { 
        chanState = false;
        return SOCKET_CLOSED;
    }
    return SUCCESS;
}
int AndroidNode::recv_msg(Message* hMsg, int timeout) {
    
    int ret;

    fd_set data;
    struct timeval tv;
    tv.tv_sec  = timeout;
    tv.tv_usec = 0;
    FD_ZERO(&data);
    FD_SET(sockfd, &data);
        errno = 0;
    ret = select(FD_SETSIZE, &data,NULL,NULL,&tv);
    if(ret < 0) {
        cout <<"Error:recv_msg("<<errno<<"): select() - Failed"<<endl;
        return SOCKET_CLOSED;
    } else if(ret == 0) {
        return TIMEOUT;
    } else if(FD_ISSET(sockfd,&data)) {
        memset(buffer,0,MAX_MSG_SIZE);
        errno = 0;
        ret = recv(sockfd,buffer,MAX_MSG_SIZE,0);
        if(ret <= 0) {
            cout <<"Error: recv_msg("<<errno<<") - Network partner not available..."<<endl;
            chanState = false;
            return SOCKET_CLOSED;
        }
        buffer[ret-1] = '\0';
        hMsg->read(buffer, true);
        //cout <<"   raw-msg[ "<<buffer<<" ] "<<endl;
        //cout <<"android-msg[ "<<hMsg->str()<<" ] "<<endl;
    }
    return SUCCESS;
}

int AndroidNode::androidBridge(Message& msg, int ttl) {
    if((msg.message_type() != ANDROID_UNDEFINED) && (msg.size() > 2)) {
        //cout <<"----androidBridge("<<msg.size()<<")------------"<<endl;
        // push user message to android 
        int ret = send_msg(&msg);
        cout <<"Sending MSG[ "<<ret<<" ][ "<<msg.android_str()<<endl;
        if(ret == SOCKET_CLOSED) {
            return SOCKET_CLOSED;
        }
        
        //{
            Message msgIn;
            // read response from Android - 
            ret = recv_msg(&msgIn, ttl);
            if(ret == TIMEOUT) { 
                cout <<"info: recv_msg() : Timeout - resending msg "<<endl;
                ret = recv_msg(&msgIn, ttl);
                if(ret != SUCCESS) { 
                    cout <<"info: recv_msg() : Failed to recv message...giving up "<<endl;
                    return -1;
                }
            }
            if(ret == SOCKET_CLOSED) {
                close(sockfd);
                if(open() == 0) {
                    cout <<"Error: recv_msg() : reopening connection....try again later"<<endl;
                    return RESEND_MSG;
                }
                return -1;
            }
            if(msgIn.message_type() == ANDROID_BIOMETRICS) {
                showBiometrics(msgIn);
            } else if(msgIn.message_type() != ANDROID_BROADCAST) {
                cout <<"\nVoiceBox:: [ "<<msgIn.str()<<" ]"<<endl;
            }
        //}
        //msg.reset();
        return SUCCESS;
    }
    return -1;
}

int AndroidNode::push(string path, vector<string>& bioMsg) {
    int f_count = 0;
    DIR * dirp = opendir(path.c_str());
    struct dirent *dp;
    if(!dirp) { 
        cout <<"Error: push() - Faces directory inaccessible..."<<endl;
        return -1;
    }
    while ((dp = readdir(dirp)) != NULL) {
        if(strlen(dp->d_name) > 3) {
            bioMsg.push_back(dp->d_name);
            cout <<"    - "<<path<<"/"<<dp->d_name<<endl;
            f_count++;
        }
    }
    closedir(dirp);

    string file_path = "adb push "+ path + " /sdcard/faces/"; 
    int ret = system(file_path.c_str()); 
    if(ret == -1) { return ret; }
    cout <<"info: Pushed [ "<<f_count<<" ] files over to android face Engine..."<<endl; 
    return SUCCESS;
}

void AndroidNode::showBiometrics(Message& msg) {
    int i = 0;
    stringstream strbuf;
    if(msg.size() < 7) {
        cout <<"error: showBio[ "<<msg.str()<<" ] "<<endl;
        return;
    }
    strbuf <<"Facial Biometric Signature:"<<endl;
    strbuf <<"         MsgType[ ANDROID_BIOMETRICS "<<endl;
    strbuf <<"        UserName[ "<<msg.entry(i++)<<endl;
    strbuf <<"           MsgId[ "<<msg.entry(i++)<<endl;
    strbuf <<"Confidence Level[ "<<msg.entry(i++)<<endl;
    strbuf <<"    EyesDistance[ "<<msg.entry(i++)<<endl;
    strbuf <<"    eyesMidPts.x[ "<<msg.entry(i++)<<endl;
    strbuf <<"    eyesMidPts.y[ "<<msg.entry(i++)<<endl;
    strbuf <<"      imageWidth[ "<<msg.entry(i++)<<endl;
    strbuf <<"     imageHeight[ "<<msg.entry(i++)<<endl;
    strbuf <<"        imageUrl[ "<<msg.entry(i++)<<endl;
    cout <<strbuf.str()<<endl;
}

void AndroidNode::voiceBox() {
    stringstream strbuf;
    string myBuffer;
    vector<string> bioMsg;

    while(true) {
        Message androidMsg;
        androidMsg.reset();

        cout <<"Enter Activity : \n  --> ";
        memset(buffer,0,MAX_MSG_SIZE);
        if(cin.getline(buffer,MAX_MSG_SIZE).eof() || (strlen(buffer) < 2)) {
            continue;
        }   
        myBuffer.assign(buffer);

        if(strncmp(myBuffer.c_str(),"-h",2) == 0) {
            usage();
            continue;
        }

        if(strncmp(myBuffer.c_str(),"-f",2) == 0) {
            char *path = strtok(strdup(myBuffer.c_str())," ");
            path = strtok(NULL," ");
            if(push(path, bioMsg) != SUCCESS) {
                cout <<"Error: pushing the Faces over to Android Face Engine...."<<endl;
                 
                continue;
            }
            cout <<"info: Successfully pushed Faces to Android Face Engine...."<<endl;
            for(uint32_t i = 0; i < bioMsg.size(); i++) {
                Message _msg(ANDROID_BIOMETRICS);
                strbuf <<ANDROID_BIOMETRICS<<":zeddy:"<<itos(message_counter++)<<":/sdcard/faces/"<<bioMsg[i];
                _msg.read(strbuf.str().c_str(), false);
                cout <<"info: push() - "<< _msg.str()<<endl;
                int ret = androidBridge(_msg, 25);
                if(ret == RESEND_MSG) {
                    cout <<"info: Network problem, resending [ "<<_msg.str()<<endl; 
                    androidBridge(_msg, 25);
                }
                _msg.reset(); 
                strbuf.str(""); 
            }
            strbuf.str(""); 
            continue;
        }

        if(androidMsg.read(myBuffer, false) != 0) {
            cout <<"Error: [ "<<buffer<<" ] AndroidMsg malformed..."<<endl;
            continue;
        }

        if(androidMsg.message_type() == ANDROID_UNDEFINED) {
            cout <<"Undefined messagetype "<<endl;
            continue;
        }
        androidMsg.insert_entry(1,itos(message_counter++));
        int ret = androidBridge(androidMsg, 15);
        if((ret != SUCCESS) && (ret != TIMEOUT)) {
            close(sockfd);
            if(open() == 0) {
                Message _msg(androidMsg.message_type());
                for(int i = 0; i < androidMsg.size()-1; i++) {
                    _msg.add_entry(androidMsg.entry(i));
                }
                cout <<"Error: send_msg() : Failed to send message....retrying ..."<<endl;
                cout <<"    msg[ "<<_msg.str()<<endl;
                androidBridge(_msg, 15);
            }
        }
        //sleep(3);
    }
}

void AndroidNode::usage() {
    string usage = "usag()::\n   VoiceClient  -h\n";
    usage += "   VoiceClient <msgType>:<clientid>:<field1>:<field2>...<fieldn>\n";
    usage += "      where msgTye {\n";
    usage += "\t[3] ANDROID_BROADCAST,  // one way voice message - \n";
    usage += "\t[4] ANDROID_QUESTION,   // Question with replay - \n";
    usage += "\t[5] ANDROID_GRID_LOCATION   // Ask humam for a desired map location - \n";
    usage += "\t[6] ANDROID_BIOMETRICS // ask Android to generate facial biometric signature - \n";
    usage += "      }\n\n";
    usage += "      e.g. 3:zeddy:Ask humam for a desired map location\n";
    usage += "      e.g. 4:zeddy:Could you help me please?\n";
    usage += "      e.g. 5:zeddy:Where is west point? Touch it on my touch Tablet.\n";
    usage += "      e.g. 6:zeddy:/sdcard/data/zeddy.png\n";
    usage += "----------------------------------------------------------"; 
    cout <<usage<<endl;
}
string AndroidNode::itos(int i) {
    stringstream ss;
    ss << i;
    return ss.str();
}

int main(int argc, char* argv[]) {
    AndroidNode android("localhost", 7500);
    android.usage();
    if(android.open() != 0) {
        cout <<"Error: Can't connect to the server....exiting..."<<endl<<endl;
        return -1;
    }
    android.voiceBox();
        
    return 0;
}
